import * as THREE from 'three';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader';
import TWEEN from '@tweenjs/tween.js';

interface ITextureInfo {
  name: string;
  url: string;
  type?: string;
}

interface IModelInfo {
  url: string;
  partsTexture: ITextureInfo[];
}
export class Application {
  public wrapper: HTMLDivElement;
  private _objLoader: OBJLoader;
  private _textureLoader: THREE.TextureLoader;
  private _renderer: THREE.WebGLRenderer | null = null;
  private _controls: OrbitControls | null = null;
  private _scene: THREE.Scene | null = null;
  private _camera: THREE.PerspectiveCamera | null = null;
  private _current3dGroup: THREE.Group | null = null;
  private raycaster: THREE.Raycaster = new THREE.Raycaster();
  private pointer: THREE.Vector2 = new THREE.Vector2();
  private sprites: THREE.Sprite[] = [];
  private shadowPlane: THREE.Mesh | null = null;

  constructor(wrapper: HTMLDivElement) {
    this.wrapper = wrapper;
    this._objLoader = new OBJLoader();
    this._textureLoader = new THREE.TextureLoader();
    this._textureLoader.crossOrigin = "anonymous";
    this.initSence();
    this.bindEvent();
  }

  initSence() {
    const boundingClientRect: DOMRect = this.wrapper.getBoundingClientRect();
    const width = boundingClientRect.width;
    const height = boundingClientRect.height;

    // render
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(width, height);
    renderer.outputEncoding = THREE.sRGBEncoding;
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 0.9;
    this.wrapper.appendChild(renderer.domElement);
    this._renderer = renderer;
    renderer.setAnimationLoop(this.animate.bind(this));

    // camera
    const target = new THREE.Vector3(0, 150, 0);
    const camera = new THREE.PerspectiveCamera(40, width / height, 0.1, 100);
    camera.position.set(-400, 400, 700);
    camera.lookAt(target);
    this._camera = camera;

    //controls
    const controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true;
    controls.maxDistance = 4;
    controls.minDistance = 1;
    controls.update();

    // scene
    const scene = new THREE.Scene();
    const backgroundTexture = new THREE.TextureLoader().load('models/texture/background.jpg');
    backgroundTexture.repeat.setY(0.5);
    backgroundTexture.repeat.setX(0.5);
    backgroundTexture.offset.setY(0.25);
    backgroundTexture.offset.setX(0.25);
    scene.background = new THREE.Color(0xf1f1f1);
    scene.environment = new RGBELoader().load('models/texture/22.hdr', () => { });
    scene.environment.mapping = THREE.EquirectangularReflectionMapping;

    // shadow
    const shadowTexture = new THREE.TextureLoader().load('models/texture/shadow.png');
    shadowTexture.wrapS = shadowTexture.wrapT = THREE.RepeatWrapping;
    shadowTexture.repeat.x = 1;
    shadowTexture.repeat.y = 1;
    const shadowGeometry = new THREE.PlaneGeometry( 2, 1.6 );
    const shadowMaterial = new THREE.MeshBasicMaterial( {color: 0xffffff, opacity: 0.4, transparent: true,} );
    shadowMaterial.map = shadowTexture;
    const shadowPlane = new THREE.Mesh( shadowGeometry, shadowMaterial );
    shadowPlane.rotateX(-Math.PI / 2);
    shadowPlane.position.set(0,-0.6,0);
    scene.add( shadowPlane );
    shadowPlane.visible = false;
    this.shadowPlane = shadowPlane;


    //scene.fog = new THREE.Fog(0xcccccc, 10, 15);
    this._scene = scene;
    this._controls = controls;
    const sprite1 = this.createHotspot(1);
    const sprite2 = this.createHotspot(2);
    const sprite3 = this.createHotspot(3);
    scene.add(sprite1);
    scene.add(sprite2);
    scene.add(sprite3);
    this.sprites.push(sprite1, sprite2, sprite3);
  }

  loadLights() {
    if (this._scene) {
      const scene = this._scene;
      const hemiLight = new THREE.HemisphereLight(0xffffff, 0xcccccc, 1.2);
      hemiLight.position.set(0, 160, 0);
      scene.add(hemiLight);

      const light1 = new THREE.PointLight(0xffffff, 1.2, 240);
      light1.position.set(0, 300, 60);
      scene.add(light1);

      const light2 = new THREE.PointLight(0xffffff, 1.2, 240);
      light2.position.set(-300, 100, 30);


      const light3 = new THREE.PointLight(0xffffff, 1.2, 240);
      light3.position.set(320, 100, 30);
      scene.add(light3);

      const light4 = new THREE.PointLight(0xffffff, 1.2, 160);
      light4.position.set(0, -40, 0);
    }
  }

  animate(time: any) {
    if (
      this._renderer &&
      this._scene &&
      this._camera &&
      this._controls &&
      this._controls
    ) {
      console.log(123);
      const render = this._renderer;
      const scene = this._scene;
      const camera = this._camera;
      const controls = this._controls;
      render.render(scene, camera);
      controls.update();
      TWEEN.update(time)
    }
  }

  async addCabint(guizi: IModelInfo) {
    if (this._current3dGroup) {
      this._scene?.remove(this._current3dGroup);
    }
    this.sprites.forEach(e => {
      e.userData.isOpen = false;
      e.userData.runing = false;
      e.userData.active = false;
      e.visible = false;
    });
    if(this.shadowPlane){
      this.shadowPlane.visible = false;
    }
    
    this._controls?.reset();
    const current3dModel = new THREE.Group();
    this._current3dGroup = current3dModel;
    this._scene?.add(current3dModel);
    const obj3d = await this._objLoader.loadAsync(guizi.url);
    current3dModel.add(obj3d);
    obj3d.position.set(0, -0.3, 0);
    obj3d.rotation.x = -Math.PI / 2;
    obj3d.scale.multiplyScalar(0.002);
    this.replaceBatchMaterial(obj3d, guizi.partsTexture);
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    if (guizi.url.includes('guizi01')) {
      [[0, -0.13, 0.36], [0, 0.12, 0.36], [0, 0.37, 0.36]].forEach((pos, index) => {
        that.sprites[index].position.set(pos[0], pos[1], pos[2]);
        that.sprites[index].userData.active = true;
        that.sprites[index].visible = true;
      });
    } else {
      [[0, -0.10, 0.36], [0, 0.30, 0.36]].forEach((pos, index) => {
        that.sprites[index].position.set(pos[0], pos[1], pos[2]);
        that.sprites[index].userData.active = true;
        that.sprites[index].visible = true;
      });
    }
    if(this.shadowPlane){
      this.shadowPlane.visible = true;
    }


    return obj3d;
  }

  async addPart(peijian: IModelInfo) {
    const obj3d = await this._objLoader.loadAsync(peijian.url);
    obj3d.visible = false;
    this._current3dGroup?.add(obj3d);
    obj3d.rotation.x = -Math.PI / 2;
    obj3d.scale.multiplyScalar(0.002);
    this.replaceBatchMaterial(obj3d, peijian.partsTexture);

    return obj3d;
  }

  removeModel(model: THREE.Group) {
    this._current3dGroup?.remove(model);
  }

  replaceMaterial(group: THREE.Group, textureInfo: ITextureInfo) {
    group.traverse((obj: THREE.Object3D) => {
      if (obj.name == textureInfo.name && obj instanceof THREE.Mesh) {
        if (textureInfo.type == 'boli') {
          const glassMaterial = new THREE.MeshPhysicalMaterial({
            color: 0xffffff,
            metalness: 0.25,
            roughness: 0,
            transmission: 1.0,
          });
          obj.material = glassMaterial;
        } else {
          const texture = new THREE.TextureLoader().load(textureInfo.url);
          texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
          texture.repeat.x = 0.005;
          texture.repeat.y = 0.005;
          const material = new THREE.MeshPhysicalMaterial({
            map: texture,
            color: 0xffffff, metalness: 0, roughness: 0, clearcoat: 0, clearcoatRoughness: 0.03, sheen: 0.5
          });
          obj.material = material;
        }
      }
    });
  }

  replaceBatchMaterial(group: THREE.Group, textureInfos: ITextureInfo[]) {
    textureInfos.forEach((textureInfo) => {
      group.traverse((obj: THREE.Object3D) => {
        if (obj.name == textureInfo.name && obj instanceof THREE.Mesh) {
          if (textureInfo.name == 'boli') { // 玻璃
            const glassMaterial = new THREE.MeshPhysicalMaterial({
              color: 0xffffff,
              metalness: 0.25,
              roughness: 0,
              transmission: 1.0,
            });
            obj.material = glassMaterial;
          } else if (textureInfo.name.includes('bashou_meiguijin')) { // 把手
            const texture = new THREE.TextureLoader().load(textureInfo.url);
            const material = new THREE.MeshPhysicalMaterial({
              map: texture,
              metalness: 1, roughness: 0.1, clearcoat: 0, clearcoatRoughness: 0.03, sheen: 1, reflectivity: 1
            });
            const color = 0xFFE0AA;
            material.color = new THREE.Color(color);
            obj.material = material;
          } else if (textureInfo.name.includes('buxiugang')) { //导轨不锈钢
            const material = new THREE.MeshPhysicalMaterial({
              metalness: 1, roughness: 0, clearcoat: 0, clearcoatRoughness: 0.03, sheen: 0, reflectivity: 0
            });
            const color = 0xFFFFFF;
            material.color = new THREE.Color(color);
            obj.material = material;
          }

          else {
            let color = 0xffffff;
            const texture = new THREE.TextureLoader().load(textureInfo.url);
            texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
            texture.repeat.x = 1;
            texture.repeat.y = 1;
            const material = new THREE.MeshPhysicalMaterial({
              map: texture,
              metalness: 1, roughness: 0.50, clearcoat: 0, clearcoatRoughness: 0.03, sheen: 0.5
            });
            if (textureInfo.url.includes('fenseyouqi')) {
              color = 0xF3BFCC;
            } else if (textureInfo.url.includes('baiseyouqi')) {
              color = 0xF5F2E5;
            } else if (textureInfo.url.includes('shabilimupi')) {
              color = 0xED7408
            } else if (textureInfo.url.includes('xiangmuzuobaiyou')) {
              color = 0xF7F5DE
            } else if (textureInfo.url.includes('pige')) {
              // const bumptexture = new THREE.TextureLoader().load('models/texture/aotu.jpg');
              // bumptexture.wrapS = bumptexture.wrapT = THREE.RepeatWrapping;
              // bumptexture.repeat.x = 1;
              // bumptexture.repeat.y = 1;
              // material.bumpMap = bumptexture;
              // material.bumpScale = -1;
              // material.roughness = 1;
              if (textureInfo.url.includes('pige_hei')) {
                color = 0xFFFFFF;
              } else if (textureInfo.url.includes('pige_fenhong')) {
                color = 0xC1797F;
              } else if (textureInfo.url.includes('pige388')) {
                color = 0xFFEDB7;
              }
            }
            material.color = new THREE.Color(color);
            obj.material = material;
          }
        }
      });
    });
  }

  createHotspot(index: number) {
    const map = new THREE.TextureLoader().load("models/texture/hotspot.png");
    const material = new THREE.SpriteMaterial({
      map: map,
      transparent: true
    });

    const sprite = new THREE.Sprite(material);
    sprite.scale.set(0.12, 0.12, 0.12);
    const spriteTween = new TWEEN.Tween({
      scale: 0.12
    }).to({
      scale: 0.06
    }, 1000).easing(TWEEN.Easing.Quadratic.Out);
    spriteTween.onUpdate(function (that) {
      sprite.scale.set(that.scale, that.scale, that.scale);
    });
    spriteTween.yoyo(true);
    spriteTween.repeat(Infinity);
    spriteTween.start();
    sprite.name = `hotspot0${index}`;
    sprite.visible = false;
    return sprite;
  }

  bindEvent() {
    // eslint-disable-next-line @typescript-eslint/no-this-alias
    const that = this;
    this.wrapper.onclick = (event: MouseEvent) => {
      if (that._camera && that._scene) {
        const bound = that.wrapper.getBoundingClientRect()
        that.pointer.x = (event.clientX / bound.width) * 2 - 1;
        that.pointer.y = - ((event.clientY - bound.top) / bound.height) * 2 + 1;
        that.raycaster.setFromCamera(that.pointer, that._camera);
        const intersects = that.raycaster.intersectObjects([...that._scene.children], false);
        if (intersects.length > 0 && that._current3dGroup) {
          const sprite = intersects[0].object;
          if (!(sprite instanceof THREE.Sprite)) return;
          const index = sprite.name.replace(/[^\d]/g, "");
          const menban = that._current3dGroup.getObjectByName(`${index}menban`);
          const chouti = that._current3dGroup.getObjectByName(`${index}chouti`);
          const bashou = that._current3dGroup.getObjectByName(`${index}bashou_meiguijin`);
          const daogui1 = that._current3dGroup.getObjectByName(`${index}daogui03_buxiugang`);
          const daogui2 = that._current3dGroup.getObjectByName(`${index}daogui02_buxiugang`);
          const suliao = that._current3dGroup.getObjectByName(`${index}daogui02_suliao`);
          const choutiPart = that._current3dGroup.getObjectByName(`part${index}`);
          if (sprite.userData.runing) return;
          sprite.userData.isOpen = !sprite.userData.isOpen;
          const isOpen = sprite.userData.isOpen;

          [menban, chouti, bashou, daogui1].filter(e => !!e).forEach(e => {
            if (isOpen) {
              that.setAnimationDoor({ y: 0 }, { y: -300 }, e, "y");
            } else {
              that.setAnimationDoor({ y: -300 }, { y: 0 }, e, "y");
            }

          });
          [suliao, daogui2].filter(e => !!e).forEach(e => {
            if (isOpen) {
              that.setAnimationDoor({ y: 0 }, { y: -150 }, e, "y");
            } else {
              that.setAnimationDoor({ y: -150 }, { y: 0 }, e, "y");
            }

          });
          if (isOpen) {
            if (choutiPart) {
              that.setAnimationDoor({ z: -0.025 }, { z: 0.575 }, choutiPart, "z");
            }
            that.setAnimationDoor({ z: 0.36 }, { z: 0.96 }, sprite, "z");
          } else {
            if (choutiPart) {
              that.setAnimationDoor({ z: 0.575 }, { z: -0.025 }, choutiPart, "z");
            }
            that.setAnimationDoor({ z: 0.96 }, { z: 0.36 }, sprite, "z");
          }

        }
      }
    }
    // this.wrapper.onmousemove = (event: MouseEvent) => {
    //   if (that._camera && that._scene) {
    //     const bound = that.wrapper.getBoundingClientRect()
    //     that.pointer.x = (event.clientX / bound.width) * 2 - 1;
    //     that.pointer.y = - ((event.clientY - bound.top) / bound.height) * 2 + 1;
    //     that.raycaster.setFromCamera(that.pointer, that._camera);

    //     if (that._current3dGroup?.children[0]) {
    //       const intersects = that.raycaster.intersectObjects([...that._current3dGroup.children[0].children, ...that.sprites], false);
    //       that.sprites.forEach(e => e.userData.active && (e.visible = intersects.length > 0));
    //       if (intersects.length > 0) {
    //         const object = intersects[0].object;
    //         if (object instanceof THREE.Sprite) {
    //           if (object.material.map?.image?.src && !object.material.map.image.src.includes('hotspotActive.png')) {
    //             object.material.map = new THREE.TextureLoader().load("models/texture/hotspotActive.png");
    //             object.scale.set(0.12, 0.12, 0.12);
    //           }
    //           document.body.style.cursor = 'pointer';
    //         }else{
    //           that.sprites.forEach(e => {
    //             if (e.material.map?.image?.src && !e.material.map.image.src.includes('hotspot.png')) {
    //               e.material.map = new THREE.TextureLoader().load("models/texture/hotspot.png");
    //               e.scale.set(0.05, 0.05, 0.05);
    //             }
  
    //           });
    //           document.body.style.cursor = 'default';
    //         }
    //       } else {
    //         that.sprites.forEach(object => {
    //           if (object.material.map?.image?.src && !object.material.map.image.src.includes('hotspot.png')) {
    //             object.material.map = new THREE.TextureLoader().load("models/texture/hotspot.png");
    //             object.scale.set(0.05, 0.05, 0.05);
    //           }

    //         });
    //         document.body.style.cursor = 'default';
    //       }
    //     }
    //   }
    // }
  }

  setAnimationDoor(start: any, end: any, mesh: any, prop: string) {
    const tween = new TWEEN.Tween(start).to(end, 1500).easing(TWEEN.Easing.Quadratic.Out);
    mesh.userData.runing = true;
    tween.onUpdate((that) => {
      mesh.position[prop] = that[prop];
    });
    tween.onComplete((that) => {
      mesh.userData.runing = false;
    });
    tween.start();
  }

  isRuning(index?: number) {
    if (index !== undefined) {
      return this.sprites[0].userData.runing;
    }
    return this.sprites.some(e => e.userData.runing);
  }
  isOpen(index?: number) {
    if (index !== undefined) {
      return this.sprites[index].userData.isOpen;
    }
    return this.sprites.some(e => e.userData.isOpen);
  }
  dispose(){
    this._renderer?.dispose();
    this._renderer = null;
    this._scene = null;
    this._controls?.dispose();
    this._controls = null;
  }

}
